﻿// SyntheticDataGenerator.Core.TaxonomyItemSet
//
// (c) 2011 Arthur Pitman
//
// Part of Market-Basket Synthetic Data Generator
// A C# adaptation of the IBM Quest Market-Basket Synthetic Data Generator
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// (Version 2) as published by the Free Software Foundation.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License (Version 2) along with this program; if not, write to 
// the Free Software Foundation, Inc., 
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// or see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace SyntheticDataGenerator.Core
{
	/// <summary>
	/// An item set based on a taxonomy
	/// </summary>
	public class TaxonomyItemSet : ItemSet
	{
		/// <summary>
		/// The taxonomy used to build this itemset
		/// </summary>
		protected Taxonomy taxonomy;

		/// <summary>
		/// Cumulative probabilities of choosing a child
		/// </summary>
		protected double[] taxonomyProbabilities;        

		public TaxonomyItemSet(int itemCount, Taxonomy taxonomy)
			: base(itemCount)
		{
			this.taxonomy = taxonomy;
			taxonomyProbabilities = new double[itemCount];

			// normalize probabilities for the roots and for children
			Normalize(probabilities, 0, taxonomy.RootCount - 1);
			for (int i = 0; (i < itemCount) && (taxonomy.GetChildCount(i) > 0); i++)
				Normalize(probabilities, taxonomy.GetFirstChild(i), taxonomy.GetLastChild(i));

			// calulate cumulative probabilities for children
			for (int i = 0; i < itemCount; i++)
				taxonomyProbabilities[i] = probabilities[i];

			for (int i = 1; i < itemCount; i++)
			{
				if (taxonomy.GetChildCount(i) > 0)
				{
					for (int j = taxonomy.GetFirstChild(i); j < taxonomy.GetLastChild(i); j++)
					{
						taxonomyProbabilities[j + 1] += taxonomyProbabilities[j];
					}
				}
			}

			// set real probabilities
			for (int i = taxonomy.RootCount; i < itemCount; i++)
				probabilities[i] *= probabilities[taxonomy.Structure[i]] * taxonomy.DepthRatio;

			NormalizeAndCalculateCumulativeProbabilities();
		}

		/// <summary>
		/// Specializes an item according to the associated <see cref="Taxonomy"/>
		/// </summary>
		/// <param name="item">The item to specialize</param>
		/// <returns>The specialized item</returns>
		public int Specialize(int item)
		{
			int childCount = taxonomy.GetChildCount(item);

			// if no children, just return the item
			if (childCount == 0)
				return item;

			int firstChild = taxonomy.GetChild(item, 0);
			int lastChild = taxonomy.GetChild(item, childCount - 1);

			// find the desired pattern using cum_prob table
			double r = uniformGenerator.Next();
			int i = (int)(firstChild + r * childCount);
			if (i == lastChild)
				i--;
			while (i < lastChild && r > taxonomyProbabilities[i])
				i++;
			while (i > firstChild && r < taxonomyProbabilities[i - 1])
				i--;

			return Specialize(i);
		}

		/// <summary>
		/// Writes the <see cref="TaxonomyItemSet"/> to a <see cref="TextWriter"/>
		/// </summary>
		/// <param name="writer"></param>
		public override void Write(TextWriter writer)
		{
			writer.WriteLine("Items:");
			for (int i = 0; i < itemCount; i++)
			{
				writer.WriteLine("{0} {1} {2} {3}", i, probabilities[i], taxonomy.GetFirstChild(i), taxonomy.GetLastChild(i));
			}			
		}

	}
}
